﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace StyleMVVM.Data.Collections
{
	public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>
	{
		private readonly Dictionary<TKey, TValue> internalDictionary = new Dictionary<TKey, TValue>();
		private readonly object lockObject = new object();

		public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
		{
			List<KeyValuePair<TKey, TValue>> returnValue = new List<KeyValuePair<TKey, TValue>>();

			lock (lockObject)
			{
				foreach (KeyValuePair<TKey, TValue> keyValuePair in internalDictionary)
				{
					returnValue.Add(new KeyValuePair<TKey, TValue>(keyValuePair.Key, keyValuePair.Value));
				}
			}

			return returnValue.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public void Add(KeyValuePair<TKey, TValue> item)
		{
			lock (lockObject)
			{
				internalDictionary.Add(item.Key, item.Value);
			}
		}

		public void AddOrUpdate(TKey key, Func<TKey, TValue> addFunc, Func<TKey, TValue, TValue> updateFunc)
		{
			lock (lockObject)
			{
				TValue currentValue;

				if (internalDictionary.TryGetValue(key, out currentValue))
				{
					internalDictionary[key] = updateFunc(key, currentValue);
				}
				else
				{
					internalDictionary[key] = addFunc(key);
				}
			}
		}

		public void AddOrUpdate(TKey key, TValue value, Func<TKey, TValue, TValue> updateFunc)
		{
			lock (lockObject)
			{
				TValue currentValue;

				if (internalDictionary.TryGetValue(key, out currentValue))
				{
					internalDictionary[key] = updateFunc(key, currentValue);
				}
				else
				{
					internalDictionary[key] = value;
				}
			}
		}

		public void Clear()
		{
			lock (lockObject)
			{
				internalDictionary.Clear();
			}
		}

		public bool TryAdd(TKey key, TValue value)
		{
			bool returnValue = false;

			lock (lockObject)
			{
				if (!internalDictionary.ContainsKey(key))
				{
					internalDictionary.Add(key, value);

					returnValue = true;
				}
			}

			return returnValue;
		}

		public bool TryRemove(TKey key, out TValue value)
		{
			bool returnValue = false;

			lock (lockObject)
			{
				if (internalDictionary.TryGetValue(key, out value))
				{
					internalDictionary.Remove(key);

					returnValue = true;
				}
			}

			return returnValue;
		}

		public bool Contains(KeyValuePair<TKey, TValue> item)
		{
			lock (lockObject)
			{
				return internalDictionary.Contains(item);
			}
		}

		public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
		{
		}

		public bool Remove(KeyValuePair<TKey, TValue> item)
		{
			lock (lockObject)
			{
				return internalDictionary.Remove(item.Key);
			}
		}

		public int Count
		{
			get
			{
				lock (lockObject)
				{
					return internalDictionary.Count;
				}
			}
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public void Add(TKey key, TValue value)
		{
			lock (lockObject)
			{
				internalDictionary.Add(key, value);
			}
		}

		public bool ContainsKey(TKey key)
		{
			lock (lockObject)
			{
				return internalDictionary.ContainsKey(key);
			}
		}

		public bool Remove(TKey key)
		{
			lock (lockObject)
			{
				return internalDictionary.Remove(key);
			}
		}

		public bool TryGetValue(TKey key, out TValue value)
		{
			lock (lockObject)
			{
				return internalDictionary.TryGetValue(key, out value);
			}
		}

		public TValue this[TKey key]
		{
			get
			{
				lock (lockObject)
				{
					return internalDictionary[key];
				}
			}
			set
			{
				lock (lockObject)
				{
					internalDictionary[key] = value;
				}
			}
		}

		public ICollection<TKey> Keys
		{
			get
			{
				lock (lockObject)
				{
					return new List<TKey>(internalDictionary.Keys);
				}
			}
		}

		public ICollection<TValue> Values
		{
			get
			{
				lock (lockObject)
				{
					return new List<TValue>(internalDictionary.Values);
				}
			}
		}
	}
}